#lang racket (require rackunit) (require "extras.rkt") ;; starting point: 10-9-interfaces-revisited.rkt ;; This illustrates the use of interfaces to hide object ;; implementations (define StupidRobot<%> (interface () ;; a new StupidRobot<%> is required to start at position 0 ;; -> StupidRobot<%> ;; RETURNS: a Robot just like this one, except moved one position ;; to the right move-right ;; -> Integer ;; RETURNS: the current x-position of the robot get-pos ;;;;;;;;;;;;;;;; items below here are for testing only ;; -> Integer for-test:get-version )) ;; Here are 3 implementations of StupidRobot<%>. ;; represent the position by a field x. (define Robot1% (class* object% (StupidRobot<%>) (init-field [x 0]) ;; interp: the position of the robot. (super-new) (define/public (move-right) (new Robot1% [x (+ x 1)])) (define/public (get-pos) x) ;; for testing only (define/public (for-test:get-version) 1) )) (define Robot2% (class* object% (StupidRobot<%>) (init-field [y 0]) ;; interp: the negative of the position of ;; the robot. (super-new) (define/public (move-right) (new Robot2% [y (- y 1)])) (define/public (get-pos) (- y)) ;; for testing only (define/public (for-test:get-version) 2) )) (define Robot3% (class* object% (StupidRobot<%>) (init-field [x empty]) ;; a list whose length is equal to the ;; position of the robot ;; Puzzle: if the robot could move left, and therefore reach ;; negative positions, how could you adapt this representation to ;; handle this? (super-new) (define/public (move-right) (new Robot3% [x (cons 1 x)])) (define/public (get-pos) (length x)) ;; for testing only (define/public (for-test:get-version) 3) )) ;; -> StupidRobot<%> (define (new-robot) (local ((define i (random 3))) ;; generates numbers in [0,k-1] (cond [(= i 0) (new Robot1%)] [(= i 1) (new Robot2%)] [(= i 2) (new Robot3%)]))) (define RobotFactory<%> (interface () make-robot ; -> StupidRobot<%> for-test:get-count ;; -> Integer. Returns the number of robots built so far )) ;; A RobotFactory% represents a Robot Factory ;; A RobotFactory is a (new RobotFactory%) [no init fields] (define RobotFactory% (class* object% (RobotFactory<%>) (field [n 0]) ;; interp: the number of StupidRobots built so far (super-new) ;; RETURNS: a new StupidRobot<%> ;; EFFECT: increment n in order to maintain the invariant. (define/public (make-robot) (set! n (+ n 1)) (local ((define which-one (modulo n 3))) (cond [(= which-one 0) (new Robot1%)] [(= which-one 1) (new Robot2%)] [(= which-one 2) (new Robot3%)]))) (define/public (for-test:get-count) n) )) (define factory1 (new RobotFactory%)) (begin-for-test (check-equal? (send factory1 for-test:get-count) 0) (send factory1 make-robot) (check-equal? (send factory1 for-test:get-count) 1) (send factory1 make-robot) (check-equal? (send factory1 for-test:get-count) 2) (send factory1 make-robot) (check-equal? (send factory1 for-test:get-count) 3) ) (begin-for-test (local ((define factory2 (new RobotFactory%))) ;; make sure the factory is fresh (check-equal? (send factory2 for-test:get-count) 0) (local ((define counts (map (lambda (n) (begin (send factory2 make-robot) (send factory2 for-test:get-count))) '(1 2 3 4 5 6)))) (check-equal? (length counts) 6) (check-equal? (send factory2 for-test:get-count) 6) (check-equal? counts '(1 2 3 4 5 6)))) (local ((define factory2 (new RobotFactory%))) ;; make sure the factory is fresh (check-equal? (send factory2 for-test:get-count) 0) (local ((define robots (map (lambda (n) (send factory2 make-robot)) '(1 2 3 4 5 6 7)))) (check-equal? (length robots) 7) (check-equal? (send factory2 for-test:get-count) 7) (check-equal? (map (lambda (r) (send r for-test:get-version)) robots) '(2 3 1 2 3 1 2)))) )